package luccy.registry;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import luccy.annotations.Component;
import luccy.annotations.Inject;
import luccy.annotations.Prototype;
import luccy.annotations.Singleton;
import luccy.behavior.Awares;
import luccy.behavior.Behavior;
import luccy.behavior.ComponentBehavior;
import luccy.behavior.InjectBehavior;
import luccy.behavior.PrototypeBehavior;
import luccy.behavior.SingletonBehavior;
import luccy.desc.AnnotationDesc;
import luccy.desc.BeanDesc;
import luccy.desc.BeanDescFactory;
import luccy.desc.ClassDesc;
import luccy.desc.PropertyDesc;
import luccy.exception.NoComponentAnnotationException;
import luccy.util.AssertionUtil;

public class RegistryImpl implements Registry {

	private Map<Class, BeanDesc> beanDescMap = new ConcurrentHashMap<Class, BeanDesc>();

	@SuppressWarnings("unchecked")
	public <T> T get(Class<? extends T> key) {
		final BeanDesc<T> beanDesc = beanDescMap.get(key);
		if (beanDesc == null) {
			return null;
		}
		// list all annotation from descs.
		final ClassDesc<T> classDesc = beanDesc.getClassDesc();
		T t = null;
		for (AnnotationDesc ad : beanDesc.getAnnotationDescList(
				ElementType.TYPE, classDesc)) {
				final Class<? extends Annotation> annotationType = ad
						.getAnnotationType();
				final Behavior runtimeBehavior = getBehavior(annotationType);
				Awares.prepareAware(runtimeBehavior, this, classDesc);
				t = runtimeBehavior.run(classDesc.getComponentClass(), null);
		}
		for (PropertyDesc pd : beanDesc.getAllPropertyDesc()) {
			final List<AnnotationDesc> annotationDescList = beanDesc.getAnnotationDescList(
								ElementType.METHOD, pd);
			for (AnnotationDesc ad : annotationDescList) {
					final Class<? extends Annotation> annotationType = ad
							.getAnnotationType();
					final Behavior runtimeBehavior = getBehavior(annotationType);
					Awares.prepareAware(runtimeBehavior, this, pd);
					t = runtimeBehavior.run(classDesc.getComponentClass(), t);
			}
		}
		return t;
	}

	public <T> void register(Class<T> componentClass) {
		AssertionUtil.assertNotNull(componentClass);
		if (componentClass.getAnnotation(Component.class) == null) {
			throw new NoComponentAnnotationException(componentClass);
		}
		if(BeanDescFactory.contains(componentClass)) {
			return;
		}
		final BeanDesc<T> beanDesc = BeanDescFactory
				.getBeanDesc(componentClass);
		final ClassDesc<T> classDesc = beanDesc.getClassDesc();
		final List<Class> dependencyClassesList = classDesc
				.getDependencyClassesList();
		for (Class key : dependencyClassesList) {
			beanDescMap.put(key, beanDesc);
		}
	}

	public void destroy() {
	}

	public void init() {
		AnnotationRegistry.register(Component.class, ComponentBehavior.class);
		AnnotationRegistry.register(Inject.class, InjectBehavior.class);
		AnnotationRegistry.register(Singleton.class, SingletonBehavior.class);
		AnnotationRegistry.register(Prototype.class, PrototypeBehavior.class);
	}

	protected Behavior getBehavior(
			final Class<? extends Annotation> annotationType) {
		return AnnotationRegistry.getBehavior(annotationType);
	}
}
